﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Core.IO;
using Core.Util;

namespace GPSCrowdSource.DataStructures {

  /// <summary>
  /// Does a time binning of gps data by grouping everything into a time of the hour of day.
  /// </summary>
  public class TimeSpaceBin : IEnumerable<TimeSpaceCollection> {

    #region Private

    // Each bin is the hour of the day from 0 - 23.
    private Dictionary<TimeBin,TimeSpaceCollection> myBins = new Dictionary<TimeBin,TimeSpaceCollection>();
    
    private int myCount = 0;

    #endregion

    #region Constructors

    public TimeSpaceBin() {}
    
    /// <summary>
    /// Create a full binning from the given collection.
    /// </summary>
    public TimeSpaceBin(IEnumerable<GeoPosition> inPos) {
      try {
        foreach(GeoPosition gp in inPos) { Add(gp); }
        }
      catch(Exception ex) { Log.Error(ex); }
      }
    
    #endregion

    #region Methods

    /// <summary>
    /// Add a shape to the time space bin.
    /// </summary>
    public void Add(GeoPosition[] inGP) {
      if(inGP.Length == 1) Add(inGP[0]);
      for(int i=0;i<inGP.Length;i++) Add(inGP[i]);
      }

    /// <summary>
    /// Add the geo position.
    /// </summary>
    public void Add(GeoPosition inGP) {
      lock(myBins) {
        myCount++;

        DateTime t = inGP.Time;
        TimeBin bin = new TimeBin(inGP.Time);

        TimeSpaceCollection l;
        if(!myBins.ContainsKey(bin)) {
          myBins.Add(bin,l = new TimeSpaceCollection(bin));
          }
        else
          l = myBins[bin];
        
        l.Add(inGP);
        }

      }

    #endregion

    #region Properties

    /// <summary>
    /// Get the collection for the given hour and day
    /// </summary>
    public TimeSpaceCollection this[TimeBin inBin] {
      get {
        if(myBins.ContainsKey(inBin))
          return myBins[inBin];

        return new TimeSpaceCollection(TimeBin.Empty);
        }
      }

    /// <summary>
    /// Get the number of bins possible.
    /// </summary>
    public int Count { get { return myBins.Count; }}

    #endregion

    #region IEnumerable
    
    public IEnumerator<TimeSpaceCollection> GetEnumerator() {
      return myBins.Values.GetEnumerator();
      }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
      return myBins.Values.GetEnumerator();
    }
    #endregion

    }

  #region Structures

  /// <summary>
  /// Structure used to describe a time bin.
  /// </summary>
  public struct TimeBin {
    
    /// <summary>
    /// Empty value.
    /// </summary>
    public static readonly TimeBin Empty = new TimeBin(int.MinValue,int.MinValue);

    #region Constructors

    public TimeBin(DateTime inTime) : this(inTime.DayOfYear,inTime.Hour) {

      }

    public TimeBin(int dayOfYear, int hour) : this() {
      DayOfyear = dayOfYear;
      Hour = hour;
      }

    #endregion

    #region Properties

    /// <summary>
    /// Get the day of the year for this bin
    /// </summary>
    public int DayOfyear { get; set; }
    
    /// <summary>
    /// Get the 0-24 hour of this time bin.
    /// </summary>
    public int Hour { get; set; }
    
    /// <summary>
    /// Get the unique id of this time bin
    /// </summary>
    public float Value { 
      get {
        float dy = DayOfyear;
        float h = (float)Hour;
        dy += (float)((h > 9)?h/100.0:h/10.0);
        return dy;
        }
      }

    /// <summary>
    /// Ask if this instance is the null instance, or empty instance.
    /// </summary>
    public bool IsEmpty { get { return DayOfyear == Empty.DayOfyear && Hour == Empty.Hour; }}

    /// <summary>
    /// Convert this time bin to a time span object.
    /// </summary>
    public TimeSpan Duration {
      get { return new TimeSpan(DayOfyear,Hour,0,0); }
      }

    #endregion

    #region Operator Overloads
    public static bool operator==(TimeBin b1, TimeBin b2) {
      return b1.Value == b2.Value;
      }

    public static bool operator!=(TimeBin b1, TimeBin b2) {
      return b1.Value != b2.Value;
      }

    #endregion

    #region Object Overrides
    public override bool Equals(object obj) {
      return Value == ((TimeBin)obj).Value;
      }

    public override int GetHashCode() {
      return ToString().GetHashCode();
      }

    public override string ToString() {
      return "" + Value;
      }

    #endregion

    }



  #endregion


  }
